package affd.logic;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;

public class Logic {
	
	public static final String ARGUMENT_DEBUG = "debug";
	
	public static final String CONFIG_FILENAME = "config.txt";
	
	private static Logger logger = Logger.getLogger("Logic");
	
	/**
	 * Creates command line parser options.
	 * 
	 * @return Command line options.
	 */
	@SuppressWarnings("static-access")
	public static Options getOptions() {
		Options options = new Options();
		
		// Create command line options
		Option input = OptionBuilder.withArgName("file").hasArgs().isRequired().create("input");
		Option output = OptionBuilder.withArgName("file").hasArgs().isRequired().create("output");	
		Option ignore = OptionBuilder.withArgName("file").hasArg().create("ignore");
		Option anonymize = OptionBuilder.withArgName("file").hasArg().create("anonymize");
		Option clean = OptionBuilder.create("clean");
		Option debug = OptionBuilder.create("debug");
		Option level = OptionBuilder.withArgName("number").hasArg().create("level");
		
		// Add options
		options.addOption(input);
		options.addOption(output);
		options.addOption(ignore);
		options.addOption(anonymize);
		options.addOption(clean);
		options.addOption(debug);
		options.addOption(level);

		return options;
	}

	/**
	 * Main method of the Logic application.
	 * 
	 * @param args Command line arguments.
	 */
	public static void main(String[] args) {
		try {
			// Parse command line arguments
		    CommandLineParser commandLineParser = new GnuParser();
	        CommandLine commandLine = commandLineParser.parse(getOptions(), args);
	        
	        final String[] inputFilenames = commandLine.getOptionValues("input");
	        final String[] outputFilenames = commandLine.getOptionValues("output");
	        final String ignoreFilename = commandLine.getOptionValue("ignore", null);
	        final String anonymizeFilename = commandLine.getOptionValue("anonymize", null);

	        final boolean clean = commandLine.hasOption("clean");
	        final boolean debug = commandLine.hasOption("debug");
	        
	        final int level = Integer.parseInt(commandLine.getOptionValue("level", "0"));
			
			// Load configurations
			Config config = new Config(CONFIG_FILENAME);
			String converterHost = config.getValue("ConverterHost");
			String converterPort = config.getValue("ConverterPort");
			
			// Initialize converter client
			ConverterClient converter = new ConverterClient(converterHost, converterPort);
			if(debug) {
				converter = new ConverterClientDebug(8100);
			}
			
			// Initialize tokenizer
			Tokenizer tokenizer = new Tokenizer(config.getValue("Punctuations"), config.getValue("PunctuationsEnd"));

			/*
			 * Creates the HfstStarter and the delete and ignore lists
			 */
			HfstStarter hfstStarter = new HfstStarter();
			HashSet<String> deleteList = null;
			HashSet<String> ignoreList = null;
			
			if (ignoreFilename != null) {
				ignoreList = new HashSet<String>();
				createScanner(hfstStarter, ignoreList, ignoreFilename);
			}
			if (anonymizeFilename != null) {
				deleteList = new HashSet<String>();
				createScanner(hfstStarter, deleteList, anonymizeFilename);
			}

			// Initialize anonymizer components
			NameReplacementFI replacer = new NameReplacementFI();
			NameAnonymizer nameAnonymizer = new NameAnonymizer(replacer, hfstStarter, level);
			nameAnonymizer.setDeleteAndAnonymizeLists(deleteList, ignoreList);
			
			SocialSecurityNumberAnonymizer ssnAnonymizer = new SocialSecurityNumberAnonymizer();			
			ssnAnonymizer.setIgnoreList(ignoreList);
			
			PhoneNumberAnonymizer pnAnonymizer = new PhoneNumberAnonymizer();
			
			EmailAnonymizer emailAnonymizer = new EmailAnonymizer(replacer);
			
			// Process all input files
			for(int i = 0; i < inputFilenames.length && i < outputFilenames.length; i++) {
				try	{
					// Convert the document
					String htmlFilename = converter.convert(inputFilenames[i], ConverterClient.EXTENSION_HTML);
		
					// Read
					HtmlDocument document = new HtmlDocument(htmlFilename, tokenizer);
					document.read();

					// Get document sentences
					ArrayList<Sentence> sentences = document.getSentences();
		
					// Anonymize
					nameAnonymizer.process(sentences); 
					ssnAnonymizer.process(sentences);
					pnAnonymizer.process(sentences);
					emailAnonymizer.process(sentences);
							
					// Write to the output html file
					String outputFilename = ConverterClient.setExtension(outputFilenames[i], ConverterClient.EXTENSION_HTML);
					document.write(outputFilename, clean);
					document.writeModified(outputFilenames[i], level);
					
					String outputExtension = ConverterClient.getExtension(outputFilenames[i]);
					if(!outputExtension.equalsIgnoreCase(ConverterClient.EXTENSION_HTML)) {
						// Convert output document
						converter.convert(outputFilename, outputExtension);
					}
					
					// Cleanup
					document.cleanup(inputFilenames[i]);
				} catch(LogicException e) {
					logger.log(Level.WARNING, "Failed to process file " + inputFilenames[i] + " !", e);
				}
			}
		}
		catch(Exception e) {
			logger.log(Level.SEVERE, "Failed to process logic !", e);
			System.exit(-1);		
		}
		
		System.exit(0);
	}


	/**
	 * Reads the words from a text file and saves the words in their possible basic forms 
	 * (if those are found, otherwise only the word itself) to the given HashSet.
	 * 
	 * @param hfstStarter The HfstStarter which is used for getting the basic forms of the words
	 * @param set The HashSet to which the words from the files are saved
	 * @param fileName The file name from which the words are read
	 * @throws IOException If a file is not found with the given file name, the IOException is thrown
	 */
	private static void createScanner(HfstStarter hfstStarter, HashSet<String> set, String fileName) throws IOException {
		//BufferedReader scanner = new BufferedReader(new InputStreamReader(fileName, "UTF8"));
		BufferedReader scanner = Utilities.createReader(fileName, null);
	    try {
	    	String line = scanner.readLine();
	    	ArrayList<String> basicForms;
	    	
	    	while (line != null){
	    		String[] words = line.split(" ");
	    		
	    		for(String word : words) {
	    			if(word.isEmpty())
	    				continue;
	    			
		    		basicForms = hfstStarter.getBasicForms(word);
		    		
		    		if (basicForms != null && basicForms.size() > 0) {
			    		for (String s : basicForms) {
				    		set.add(s);
			    		}
		    		
		    		} else if (basicForms.size() == 0 && word.length() >= 2) {
		    			String firstLetterUpper = word.substring(0, 1).toUpperCase() + word.substring(1);
			    		basicForms = hfstStarter.getBasicForms(firstLetterUpper);
			    		if (basicForms != null && basicForms.size() > 0) {
				    		for (String s : basicForms) {
					    		set.add(s.toLowerCase());
				    		}
			    		} else {
			    			set.add(word);
			    		}
	
		    		} else {
		    			set.add(word);
		    		}
		    	}
	    		line = scanner.readLine();
	    	}
	    }
	    finally {
	      scanner.close();
	    }		
	}
}
